﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Reflection;
using System.Linq;
using System.Text.RegularExpressions;
using System.Net.NetworkInformation;


namespace AgileDesign.Utilities
{

    public class ConfigPropertyTest
    {
        const string SuccessMessage = "Success";
        public string Name { get; set; }
        public string ConnectionString { get; set; }
        public string StatusMessage { get; set; }

        public ConfigPropertyTest()
        {
        }

        public ConfigPropertyTest(KeyValuePair<string, object> pair)
        {
            Name = pair.Key;
            ConnectionString = GetValue(pair);
            StatusMessage = HidePassword(CheckStatus(GetValue(pair)));
        }

        private static string GetValue(KeyValuePair<string, object> p)
        {
            return (p.Value ?? "null").ToString();
        }

        private static string GetFullPath(string path)
        {
            try
            {
                if (path != null
                    && ! Path.IsPathRooted(path))
                {
                    string executingDirectoryPath = AppDomain.CurrentDomain.BaseDirectory;
                    if ( ! String.IsNullOrEmpty(AppDomain.CurrentDomain.RelativeSearchPath))
                    {
                        executingDirectoryPath = AppDomain.CurrentDomain.RelativeSearchPath;
                    }
                    path = Path.GetFullPath(Path.Combine(executingDirectoryPath, path));
                }
            }
            catch (Exception)
            {
            }
            return path;
        }

        private static string CheckStatus(string inputString)
        {
            if (String.IsNullOrEmpty(inputString)
               || inputString == "null")
            {
                return "";
            }
            if (IsUrl(inputString))
            {
                return GetPingStatus(inputString);
            }
            if (IsMsSqlServerConnectionString(inputString))
            {
                return GetMsSqlStatus(inputString);
            }
            if (IsFolder(inputString))
            {
                return GetFolderWriteStatus(inputString);
            }
            if (IsFile(inputString))
            {
                return GetFileReadStatus(inputString);
            }
            return "";
        }

        private static string GetFileReadStatus(string inputString)
        {
            try
            {
                return (File.Exists(GetFullPath(inputString)))
                           ? SuccessMessage
                           : "Not found: " + GetFullPath(inputString);
            }
            catch (Exception e)
            {
                return String.Format("Not found: '{0}'; Exception='{1}'"
                                     , GetFullPath(inputString), e);
            }
        }

        private static bool IsFile(string inputString)
        {
            string normalizedInput = inputString.TrimStart('.');
            return Regex.IsMatch(normalizedInput, @"\\.*\.");
        }

        /// <summary>
        /// The assumption is that a string value is a folder if it contains "\" and does not contain "."
        /// </summary>
        private static bool IsFolder(string inputString)
        {
            return Regex.IsMatch(inputString, @"\\[^.]+$");
        }

        private static string GetFolderWriteStatus(string inputString)
        {
            StreamWriter tmpPingFile = null;
            string fullFileName = null;
            try
            {
                string fullPath = GetFullPath(inputString);
                string folderPath = fullPath;
                fullFileName = Path.Combine(
                    folderPath, Guid.NewGuid().ToString());
                tmpPingFile = File.CreateText(fullFileName);
                tmpPingFile.WriteLine(SuccessMessage);
                return SuccessMessage;
            }
            catch (Exception ex)
            {
                return ex.ToString();
            }
            finally
            {
                if (tmpPingFile != null)
                {
                    tmpPingFile.Close();
                }
                if (fullFileName != null
                    && File.Exists(fullFileName))
                {
                    File.Delete(fullFileName);
                }
            }
        }

        private static string GetMsSqlStatus(string inputString)
        {
            try
            {
                new SqlConnection(inputString).Open();
                return SuccessMessage;
            }
            catch (Exception e)
            {
                return e.ToString();
            }
        }

        private static bool IsMsSqlServerConnectionString(string inputString)
        {
            return Regex.IsMatch(inputString, "Data Source=", RegexOptions.IgnoreCase);
        }

        private static bool IsUrl(string inputString)
        {
            return Regex.IsMatch(inputString, "^http(s)?://", RegexOptions.IgnoreCase);
        }

        private static string GetPingStatus(string inputString)
        {
            var url = new Uri(inputString);
            const int timeoutMilliseconds = 3 * 1000; //3 seconds
            try
            {
                return (new Ping()).Send(url.Host, timeoutMilliseconds).Status.ToString();
            }
            catch (Exception e)
            {
                return e.ToString();
            }
        }

        /// <summary>
        /// Replaces a string with "password=" and ";" like "...password=DoNotShowMe;..." with "...password=********;..." 
        /// and a string like "...password=DoNotShowMe" with "...password=********"
        /// </summary>
        private static string HidePassword(string inputString)
        {
            return new Regex("(?<=password=)(.+(?=;)|.+$)"
                             , RegexOptions.IgnoreCase)
                .Replace(inputString, "********");
        }
    }

}